package factorytest.iflytek.com.einkiflytekfactorytest.factorytestui;

import android.Manifest;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
import factorytest.iflytek.com.einkiflytekfactorytest.BluetoothBondManager.BluetoothBondManager;
import factorytest.iflytek.com.einkiflytekfactorytest.BluetoothBondManager.OnBluetoothAllListener;
import factorytest.iflytek.com.einkiflytekfactorytest.BluetoothSco.BluetoothScoManager;
import factorytest.iflytek.com.einkiflytekfactorytest.BluetoothSco.OnBluetoothScoConnectedListener;
import factorytest.iflytek.com.einkiflytekfactorytest.R;
import factorytest.iflytek.com.einkiflytekfactorytest.common.PermissionManager;
import factorytest.iflytek.com.einkiflytekfactorytest.common.RecordAndPlayThread;
import factorytest.iflytek.com.einkiflytekfactorytest.device.DeviceAttr;
import factorytest.iflytek.com.einkiflytekfactorytest.utils.DebugLog;


public class FactoryBluetoothActivity extends FactoryBaseActivity implements
        View.OnClickListener, AdapterView.OnItemClickListener,
        CompoundButton.OnCheckedChangeListener,
        OnBluetoothAllListener, OnBluetoothScoConnectedListener {


    private final Object flagScan = new Object();
    private final Object flagScanning = new Object();
    private Button btnScan;
    private ListView listFond;
    private ListView listBond;

    private TextView txtInfo;

    private SwitchCompat btSwitch;

    private TextView txtBluetoothScoInfo;
    private Button btnTestBluetoothSco;
    private final Object flagScoStart = new Object();
    private final Object flagScoStop = new Object();

    private Button btnJumpToSelectBT;

    private Map<String, BluetoothDevice> fondBTDeviceMap = new HashMap<>();
    private Map<String, BluetoothDevice> bondBTDeviceMap = new HashMap<>();


    private BTDeviceAdapter fondDeviceAdapter;
    private BTDeviceAdapter bondDeviceAdapter;

    private BluetoothBondManager bluetoothBondManager;

    private BluetoothScoManager bluetoothScoManager;

    private String TEST_BT_NAMES[] = {
            "K2",
            "A2",
            "W-speaker",
            "N-612",
            "BT-speaker",
            "HAVIT M8",
    };

    @Override
    int getLayoutId() {
        return R.layout.activity_factory_bluetooth;
    }

    @Override
    void onInitView(@Nullable Bundle savedInstanceState) {

        btnScan = (Button) findViewById(R.id.btnScan);
        txtInfo = (TextView) findViewById(R.id.txtInfo);
        listFond = (ListView) findViewById(R.id.listFond);
        listBond = (ListView) findViewById(R.id.listBond);

        btSwitch = (SwitchCompat) findViewById(R.id.btSwitch);

        btSwitch.setThumbResource(R.drawable.switchbar_thumb);
        btSwitch.setTrackResource(R.drawable.switchbar_track);
        btSwitch.setOnCheckedChangeListener(this);


        txtBluetoothScoInfo = (TextView) findViewById(R.id.txtBluetoothScoInfo);
        btnTestBluetoothSco = (Button) findViewById(R.id.btnTestBluetoothSco);
        btnJumpToSelectBT = (Button) findViewById(R.id.btnJumpToSelectBT);

        btnScan.setOnClickListener(this);
        btnTestBluetoothSco.setOnClickListener(this);
        btnJumpToSelectBT.setOnClickListener(this);


        bluetoothBondManager = new BluetoothBondManager(mContext);
        bluetoothScoManager = new BluetoothScoManager(mContext);

        bluetoothBondManager.setEnabledListener(this);
        bluetoothBondManager.setBondListener(this);
        bluetoothBondManager.setDiscoveryListener(this);
        bluetoothBondManager.setFoundListener(this);
        bluetoothBondManager.setAclConnectedListener(this);

        bluetoothScoManager.setScoConnectedListener(this);


        if (!isBTGotPermission()) {
            initBTPermission();
        }

        if (!isRecordGotPermission()) {
            initRecordPermission();
        }

        fondDeviceAdapter = new BTDeviceAdapter(mContext, -1);
        bondDeviceAdapter = new BTDeviceAdapter(mContext, -1);

        listFond.setAdapter(fondDeviceAdapter);
        listBond.setAdapter(bondDeviceAdapter);

        listFond.setOnItemClickListener(this);
        listBond.setOnItemClickListener(this);

        setScoViewVisibility(false);
        setBtnPassVisibility(View.INVISIBLE);
    }

    @Override
    public void onClick(View v) {

        if (v == btnScan) {

            if (btnScan.getTag() == flagScanning) {

                bluetoothBondManager.cancelDiscovery();

            } else {

                startDiscovery();
            }

        } else if (v == btnJumpToSelectBT) {

            if (DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {

                setScoViewVisibility(true);

            }

            startBTSettings();

        } else if (v == btnTestBluetoothSco) {

            if (btnTestBluetoothSco.getTag() == flagScoStop) {

                bluetoothScoManager.startRecorderScoMode();

            } else {

                bluetoothScoManager.stopRecorderScoMode();

            }

        }

    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

        setBluetoothEnabled(isChecked);
    }

    private void setSwitchStatus(boolean on) {
        btSwitch.setOnCheckedChangeListener(null);
        btSwitch.setChecked(on);
        btSwitch.setOnCheckedChangeListener(this);
    }

    @Override
    protected void onResume() {
        super.onResume();

        DebugLog.e("bt onResume");

        setSwitchStatus(bluetoothBondManager.isBluetoothEnabled());

        bluetoothBondManager.registerBTReceiver();
        startDiscovery();

        if (DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {
            bluetoothScoManager.registerBluetoothScoReceiver();
        } else  {
            startTestPlaySound();
        }
    }

    @Override
    protected void onPause() {
        DebugLog.e("bt onPause");

        super.onPause();
        bluetoothBondManager.cancelDiscovery();
        bluetoothBondManager.unregisterBTReceiver();

        if (DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {
            bluetoothScoManager.unregisterBluetoothScoReceiver();
        } else  {
            stopTestPlaySound();
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        DebugLog.e("bt onWindowFocusChanged: hasFocus = " + hasFocus);
        if (hasFocus && btnTestBluetoothSco.getTag() == flagScoStart) {
            bluetoothScoManager.stopRecorderScoMode();
            bluetoothScoManager.startRecorderScoMode();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        btOnDestroy();

        DebugLog.e("bt onDestroy: disable bt");
    }

    private MediaPlayer mediaPlayer;

    private void stopTestPlaySound() {

        DebugLog.e("stopTestPlaySound ");

        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();//回收资源
            mediaPlayer = null;
        }
    }

    private void startTestPlaySound() {
        if (mediaPlayer != null) {
            stopTestPlaySound();
        }
        try {
            mediaPlayer = MediaPlayer.create(getContext(), R.raw.music_test_channel);;
            mediaPlayer.setLooping(true);//这种这个后, setOnCompletionListener不能被调用了.
            mediaPlayer.start();
            DebugLog.e("startTestPlaySound ");

        } catch (Exception e) {
            DebugLog.e("exception e = " + e.getMessage());
        }

        mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                DebugLog.e("onError, what = " + what + ", extra = " + extra);
                startTestPlaySound();
                return true;
            }
        });

    }

    private void btOnDestroy() {
        DebugLog.e("btOnDestroy");
        stopRecordAndPlay();

        if (bluetoothScoManager != null) {

            bluetoothScoManager.setScoConnectedListener(null);
            bluetoothScoManager.stopRecorderScoMode();
            bluetoothScoManager = null;
        }


        if (bluetoothBondManager != null) {

            bluetoothBondManager.setEnabledListener(null);
            bluetoothBondManager.setBondListener(null);
            bluetoothBondManager.setDiscoveryListener(null);
            bluetoothBondManager.setFoundListener(null);
            bluetoothBondManager.setAclConnectedListener(null);

            bluetoothBondManager.cancelDiscovery();
            bluetoothBondManager.disconnectAllProfile();

            Set<BluetoothDevice> deviceList = bluetoothBondManager.getBluetoothAdapter().getBondedDevices();

            if (deviceList != null && !deviceList.isEmpty()) {
                for (BluetoothDevice device : deviceList) {
                    bluetoothBondManager.removeBond(device);
                }
            }

            bluetoothBondManager.closeBluetooth();
            bluetoothBondManager = null;
        }
    }


    private void startDiscovery() {

        if (bluetoothBondManager.isBluetoothEnabled()) {

            fondBTDeviceMap.clear();
            updateFondDeviceList();

            Set<BluetoothDevice> deviceList = bluetoothBondManager.getBluetoothAdapter().getBondedDevices();

            bondBTDeviceMap.clear();

            if (deviceList != null && !deviceList.isEmpty()) {
                for (BluetoothDevice device : deviceList) {
                    bondBTDeviceMap.put(device.getAddress(), device);
                }

            }
            updateBondDeviceList();

            bluetoothBondManager.startDiscovery();

            return;
        }

        setBluetoothEnabled(true);

    }

    private void setBluetoothEnabled(boolean enabled) {

        if (enabled) {
            bluetoothBondManager.openBluetooth();

        } else {
            bluetoothBondManager.cancelDiscovery();
            bluetoothBondManager.closeBluetooth();
        }

        bondBTDeviceMap.clear();
        fondBTDeviceMap.clear();
        updateBondDeviceList();
        updateFondDeviceList();

    }


    private void setScoViewVisibility(boolean visibility) {
        txtBluetoothScoInfo.setVisibility(visibility ? View.VISIBLE : View.INVISIBLE);
        btnTestBluetoothSco.setVisibility(visibility ? View.VISIBLE : View.INVISIBLE);
    }


    private void startBTSettings() {

        if (startAndroidBluetoothSettings()) {
            return;
        }

        if (startIflytekBluetoothSettings()) {
            return;
        }

        Toast.makeText(mContext, R.string.bt_no_settings, Toast.LENGTH_LONG).show();

    }

    private boolean startAndroidBluetoothSettings() {

        try {

            Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
            startActivity(intent);

        } catch (Exception e) {

            return false;
        }

        return true;
    }

    private boolean startIflytekBluetoothSettings() {

        try {

            Intent intent = new Intent(Intent.ACTION_MAIN);
            ComponentName cn = new ComponentName("com.iflytek.ainote.settings", "com.iflytek.ainote.settings.ui.activity.BlueToothActivity");
            intent.setComponent(cn);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);

        } catch (Exception e) {

            return false;
        }

        return true;

    }

    private void initRecordPermission() {
        //如果无权限就申请然后回调，如果有权限就执行录音监听
        if (!isRecordGotPermission()) {

            //同时申请多个权限
            PermissionManager.getInstance(getApplicationContext()).execute(this,
                    android.Manifest.permission.RECORD_AUDIO,
                    android.Manifest.permission.WRITE_SETTINGS,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE);


        }

    }

    private boolean isRecordGotPermission() {

        return (ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED
        );

    }


    private boolean isBTGotPermission() {

        return (ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
        );

    }

    private void initBTPermission() {

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            //同时申请多个权限
            PermissionManager.getInstance(getApplicationContext()).execute(this,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION);

        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionManager.getInstance(getApplicationContext()).
                onRequestPermissionsResult(requestCode, permissions, grantResults);

    }

    /////////////////////////////////////////////////////////////////////////////////

    private void updateScoBtn() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {


                if (btnTestBluetoothSco.getTag() == flagScoStart) {

                    btnTestBluetoothSco.setText(R.string.bt_test_sco_stop);

                } else {

                    btnTestBluetoothSco.setText(R.string.bt_test_sco_start);

                }

            }
        });

    }

    /////////////////////////////////////////////////////////////////////////////////

    private void updateScanBtn() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                if (bluetoothBondManager.isDiscovering()) {
                    btnScan.setText(R.string.ft_bt_scanning);
                    btnScan.setTag(flagScanning);
                } else {
                    btnScan.setText(R.string.ft_bt_scan);
                    btnScan.setTag(flagScan);
                }

            }
        });

    }

    private synchronized void updateFondDeviceList() {
        fondDeviceAdapter.setNotifyOnChange(false);
        fondDeviceAdapter.clear();

        fondDeviceAdapter.setNotifyOnChange(true);

        ArrayList<BluetoothDevice> priority = new ArrayList<BluetoothDevice>();
        for (String name : TEST_BT_NAMES) {

            for (Iterator<Map.Entry<String, BluetoothDevice>> it = fondBTDeviceMap.entrySet().iterator(); it.hasNext();){
                Map.Entry<String, BluetoothDevice> item = it.next();
                BluetoothDevice device = item.getValue();

                if (TextUtils.isEmpty(device.getName())) {
                    continue;
                }
                if (name.equals(device.getName())) {
                    priority.add(device);
                    it.remove();
                }
            }
        }

        fondDeviceAdapter.addAll(priority);
        fondDeviceAdapter.addAll(fondBTDeviceMap.values());
    }

    private synchronized void updateBondDeviceList() {
        bondDeviceAdapter.setNotifyOnChange(false);
        bondDeviceAdapter.clear();

        bondDeviceAdapter.setNotifyOnChange(true);
        bondDeviceAdapter.addAll(bondBTDeviceMap.values());
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        BluetoothDevice device = null;
        if (parent == listFond) {
            device = fondDeviceAdapter.getItem(position);
        } else if (parent == listBond) {
            device = bondDeviceAdapter.getItem(position);
        }

        if (device == null) {
            return;
        }
        DebugLog.e("device addr = " + device.getAddress() + ", name = " + device.getName());

        bluetoothBondManager.bondDevice(device);

    }

    @Override
    public void onBluetoothBonding(BluetoothDevice device) {

        DebugLog.e("onBluetoothBonding device = " + device);
        String name = device.getName();
        if (TextUtils.isEmpty(name)) {
            name = device.getAddress();
        }
        String status = getString(R.string.bt_bonding_status, name);
        updateTxtInfo(status);

        bondBTDeviceMap.put(device.getAddress(), device);
        fondBTDeviceMap.remove(device.getAddress());
        updateBondDeviceList();
        updateFondDeviceList();

    }

    @Override
    public void onBluetoothBonded(BluetoothDevice device) {
        DebugLog.e("onBluetoothBonded device = " + device);

        String name = device.getName();
        if (TextUtils.isEmpty(name)) {
            name = device.getAddress();
        }

        if (DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {
            String status = getString(R.string.bt_bonded_status, name);
            updateTxtInfo(status);
        } else {
            updateTxtInfo(getString(R.string.bt_normal_test_info));
        }

    }

    @Override
    public void onBluetoothConnected(BluetoothDevice device, int profile) {


        DebugLog.e("onBluetoothConnected profile = " + profile);

        if (profile == BluetoothProfile.HEADSET) {

            getHandler().sendEmptyMessageDelayed(MSG_START_RECORD_SCO_MODE, DELAY_START_RECORD_SCO_MODE);

        }
    }

    @Override
    public void onBluetoothDisconnected(BluetoothDevice device, int profile) {
        DebugLog.e("onBluetoothDisconnected profile = " + profile);

    }

    @Override
    public void onDiscoveryStarted() {
        updateScanBtn();
    }

    @Override
    public void onDiscoveryFinished() {
        updateScanBtn();
    }

    @Override
    public void onBluetoothEnabled(boolean enabled) {
        setSwitchStatus(enabled);

        DebugLog.e("onBluetoothEnabled enable = " + enabled);
        if (enabled) {

            startDiscovery();

        } else {

            bluetoothScoManager.stopRecorderScoMode();
            stopRecordAndPlay();
            setScoViewVisibility(false);
            String status = getString(R.string.bt_status_off);
            updateTxtInfo(status);
        }
    }

    @Override
    public void onBluetoothFound(BluetoothDevice device) {

        if (fondBTDeviceMap.get(device.getAddress()) == null) {

            fondBTDeviceMap.put(device.getAddress(), device);

            updateFondDeviceList();
        }

    }

    @Override
    public void onBluetoothScoConnected(boolean connected) {
        if (!DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {
            return;
        }

        DebugLog.e("onBluetoothScoConnected connected = " + connected);

        if (connected) {
            startRecordAndPlay();
        } else {
            stopRecordAndPlay();
        }

    }

    @Override
    public void onBluetoothScoFailed() {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(mContext, R.string.bt_test_sco_connected_failed, Toast.LENGTH_LONG).show();

                finishWithFail();
            }
        });

    }

    @Override
    public void onBluetoothACLConnected(BluetoothDevice device, boolean connected) {

        DebugLog.e("onBluetoothACLConnected = " + connected);

        if (!connected) {

            onBluetoothDisconnected(device);

        } else {

            if (bondBTDeviceMap.get(device.getAddress()) != null) {
                getHandler().sendEmptyMessageDelayed(MSG_START_RECORD_SCO_MODE, DELAY_START_RECORD_SCO_MODE);
            }
        }
    }

    private void onBluetoothDisconnected(BluetoothDevice device) {

        setBtnPassVisibility(View.INVISIBLE);
        setScoViewVisibility(false);
        bluetoothScoManager.stopRecorderScoMode();
        bluetoothBondManager.removeBond(device);

        String name = device.getName();
        if (TextUtils.isEmpty(name)) {
            name = device.getAddress();
        }
        String status = getString(R.string.bt_disconnected_status, name);
        updateTxtInfo(status);

        startDiscovery();
    }

    private class BTDeviceAdapter extends ArrayAdapter<BluetoothDevice> {

        public BTDeviceAdapter(@NonNull Context context, int resource) {
            super(context, resource);
        }

        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView != null) {
                holder = (ViewHolder) convertView.getTag();
            }
            if (holder == null) {
                holder = new ViewHolder();
                convertView = getLayoutInflater().inflate(R.layout.item_ft_bt_devices, null);
                holder.txtName = (TextView) convertView.findViewById(R.id.txtName);

                convertView.setTag(holder);
            }

            BluetoothDevice device = getItem(position);

            if (device != null) {

                String name = device.getName();
                if (TextUtils.isEmpty(name)) {
                    name = device.getAddress();
                }
                holder.txtName.setText(name);

            }

            return convertView;
        }
    }

    private final class ViewHolder {
        private TextView txtName;
    }

    private void updateTxtInfo(String txt) {
        getHandler().obtainMessage(MSG_UPDATE_TXT_INFO, txt).sendToTarget();

    }

    ////////////////////////////////////////////////
    ////////////////////////////////////////////////

    private final static int MSG_UPDATE_TXT_INFO = 2000;

    private final static int MSG_SET_BTNPASS_VISIBLE = 2001;
    private final static int DELAY_SET_BTNPASS_VISIBLE = 8000;//5s

    private final static int MSG_START_RECORD_SCO_MODE = 2002;
    private final static int DELAY_START_RECORD_SCO_MODE = 2500;//2s


    @Override
    void onHandleMessage(Handler handler, Message msg) {

        switch (msg.what) {
            case MSG_UPDATE_TXT_INFO:
                if (msg.obj instanceof String) {
                    txtInfo.setText((String) msg.obj);
                }
                break;
            case MSG_SET_BTNPASS_VISIBLE:
                setBtnPassVisibility(View.VISIBLE);
                break;
            case MSG_START_RECORD_SCO_MODE:

                int delay = DELAY_SET_BTNPASS_VISIBLE / 4;

                if (DeviceAttr.getRunningDeviceAttr().hasBluetoothRecord()) {

                    setScoViewVisibility(true);
                    bluetoothScoManager.startRecorderScoMode();

                    delay = DELAY_SET_BTNPASS_VISIBLE;

                }

                handler.sendEmptyMessageDelayed(MSG_SET_BTNPASS_VISIBLE, delay);


                break;
        }

    }

///////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////

    private RecordAndPlayThread recordAndPlayThread = null;


    private void startRecordAndPlay() {

        DebugLog.e("startRecordAndPlay------------");

        btnTestBluetoothSco.setTag(flagScoStart);
        updateScoBtn();

        if (recordAndPlayThread == null) {
            recordAndPlayThread = new RecordAndPlayThread("sco_test", mContext, /*AudioManager.STREAM_BLUETOOTH_SCO*/6, false);
            recordAndPlayThread.start();
        }

    }

    private void stopRecordAndPlay() {

        DebugLog.e("stopRecordAndPlay----------------");

        btnTestBluetoothSco.setTag(flagScoStop);
        updateScoBtn();

        if (recordAndPlayThread != null) {
            recordAndPlayThread.joinThread();
            recordAndPlayThread = null;
        }

    }

}
